/*
    使用互斥锁的方式
    多线程统计 Linux 内核中 .c、.h 文件的文件数及文件的总行和
    每个文件，启动一个新线程，总行数
    启动太多的线程，可以使用信号量，对创建线程的数量进行限制。
*/

/*
    这样分配会堆空间不足，将线程设置为固定个数，等都完成之后再重新创建，重新分配任务
*/

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <string.h>
#include <pthread.h>

typedef struct wc_count
{
    int file;
    int folder;
    int link;
    int file_c;
    int file_h;
    pthread_t* tid;
    int size;
    int capacity;
    // char* filename;
}wc_count;

volatile int row_c = 0;
volatile int row_h = 0;

void wc_cInit(wc_count* wc_c);
void dp_count(char* address, wc_count* wc_c);
void wc_c_safe(wc_count* wc_c);
void* routine(void* avg);
int is_source_file(const char *filename);

//创建一把互斥锁,用来锁总的.c 和 .h行数
pthread_mutex_t mut;

int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        perror("参数传输错误！\n");
        exit(1);
    }
    pthread_mutex_init(&mut, NULL);
    wc_count wc_c;
    wc_cInit(&wc_c);
    dp_count(argv[1], &wc_c);
    for(int i = 0; i < wc_c.size; i++)
    {
        pthread_join(wc_c.tid[i], NULL);
    }
    printf("%s中%d个文件夹，%d个文件, %d个链接\n",argv[1], wc_c.folder, wc_c.file, wc_c.link);
    printf("包含%d 个 .c 文件 一共 %d 行，%d 个 .h 文件 一共 %d 行\n", wc_c.file_c, row_c, wc_c.file_h, row_h);
    pthread_mutex_destroy(&mut);
    free(wc_c.tid);
    return 0;
}

void wc_cInit(wc_count* wc_c)
{
    wc_c->file = 0;
    wc_c->file_c = 0;
    wc_c->file_h = 0;
    wc_c->capacity = 1024;
    wc_c->folder = 0;
    wc_c->link = 0;
    wc_c->size = 0;
    wc_c->tid = (pthread_t*)malloc(wc_c->capacity * sizeof(pthread_t));
}

void wc_c_safe(wc_count* wc_c)
{
    if(wc_c->size >= wc_c->capacity-1)
    {
        wc_c->capacity = 1.1*wc_c->capacity;
        wc_c->tid = (pthread_t*)realloc(wc_c->tid, wc_c->capacity * sizeof(pthread_t));
        if(wc_c->tid == NULL)
        {
            printf("扩容失败!\n");
            exit(1);
        }
    }
}

void dp_count(char* address, wc_count* wc_c)
{
    //打开目录流
    DIR *dir;
    dir = opendir(address);
    if(!dir)
    {
        perror("打开目录失败！\n");
        exit(1);
    }
    struct dirent* read_dir;
    char name[10240];
    while(read_dir = readdir(dir))
    {
        // 文件名为 . 或者 .. 的跳过
        if(!strcmp(read_dir->d_name, ".") || !strcmp(read_dir->d_name, ".."))
            continue;
        snprintf(name, sizeof(name), "%s/%s", address, read_dir->d_name);
        if(read_dir->d_type == 4)
        {
            wc_c->folder++;
            dp_count(name, wc_c);
        }
        if(read_dir->d_type == 8)
        {
            wc_c->file++;
            int len = strlen(read_dir->d_name);
            if(len >= 2) {
                const char* suffix = read_dir->d_name + len - 2;
                int is_c = !strcmp(suffix, ".c");
                int is_h = !strcmp(suffix, ".h");
                if(is_c || is_h) {
                    if(is_c)
                        wc_c->file_c++;
                    else
                        wc_c->file_h++;
                    char* fullpath = strdup(name);
                    if (!fullpath) {
                        perror("strdup failed");
                        continue;
                    }
                    int ret = pthread_create(&(wc_c->tid[wc_c->size]), NULL, routine, fullpath);
                    if (ret == 0) {
                        wc_c->size++;
                        wc_c_safe(wc_c);
                    } else {
                        perror("pthread_create failed");
                        free(fullpath);
                        exit(1);
                    }
                }
            }
        }
        if(read_dir->d_type == 10)
            wc_c->link++;
    }
    closedir(dir);
}

void* routine(void* avg)
{
    char* filename = (char*) avg;
    FILE* fp = fopen(filename, "r");
    if (!fp) {
        perror("fopen");
        free(filename); // 释放 strdup 的内存
        pthread_exit(NULL);
        exit(1);
    }
    int ch;
    int row = 0;
    while ((ch = fgetc(fp)) != EOF) {
        if (ch == '\n')
            row++;
    }
    fclose(fp);
    printf("%s 文件中有%d行数据\n", filename, row);
    int ret = is_source_file(filename);
    if(ret == 1)
    {
        //需要一把锁，锁总行数
        pthread_mutex_lock(&mut);
        //.c 文件
        row_c += row;
        pthread_mutex_unlock(&mut);
    }
    else if(ret == 2)
    {
        //需要一把锁，锁总行数
        pthread_mutex_lock(&mut);
        //.h 文件
        row_h += row;
        pthread_mutex_unlock(&mut);
    }
    free(filename); // 释放 strdup 的内存
    pthread_exit(NULL);
    return NULL;
}

// 判断是否是 .c 或 .h 文件
int is_source_file(const char *filename) {
    int len = strlen(filename);
    if (len >= 2 && filename[len - 2] == '.' && filename[len - 1] == 'c') return 1; // .c
    if (len >= 2 && filename[len - 2] == '.' && filename[len - 1] == 'h') return 2; // .h
    return 0;
}